#include <math.h>
#include <stdio.h>
#define U_INT16 unsigned short

short display_as_real;
short display_debug;

#define TRUE  1
#define FALSE 0

  /*******************************/
  /*  Generic Unit Conversion    */
  /*******************************/

#define DCM2_PER_IU   160
#define DCM2_TO_PSI   1.450377e-5

#define T_NTP         298.15
#define T_STP         273.15

#define IU_SCALING    65536

  /******************************/
  /*  Sensor Details            */
  /******************************/

/* REVISIT sensor used for manny, not final sensor to be used in IQ1 */
/* REVISIT   scaled down for manny, since adc has 4 volt input  */
#define SENSOR_OFFSET   1.00    /* volts @ 0 PSI */
#define SENSOR_SPAN     3.00    /*   4.0 volts full scale */
#define SENSOR_MAX_PSI 100.0    /* 100.0 PSI   full scale */


#define SENSOR_MAX_PRESSURE   (SENSOR_MAX_PSI/(DCM2_TO_PSI*DCM2_PER_IU))
#define SENSOR_RANGE  (SENSOR_MAX_PSI-0)



typedef struct ps_calib_struct 
               {
                   float    offset;  /* volts at 0 PSI */
                   float    span;    /* volts at full scale - volts at 0 psi */
                   float    range;   /* PSIG at full scale */
               };

struct ps_calib_struct ps_calib = { SENSOR_OFFSET,
                                    SENSOR_SPAN,
                                    SENSOR_RANGE };

typedef struct frit_calib_struct
               {
                   char     name[30];  /* color + "_" + gas */
                   float    parm1;     /* zero order term from MottFit */
                   float    parm2;     /* first order term from MottFit */
                   float    parm3;     /* second order term from MottFit */
               };

  /******************************/
  /*  Frit Details              */
  /******************************/


#define NUM_FRIT_ENTRIES 26

/* revisit  reinsert array size initializer when all frit entries converge */
struct frit_calib_struct frit_calib[/*NUM_FRIT_ENTRIES*/] = {
        {"Brown_Air",          0.68956,  5.16545,   0.122541 },

        {"Black_He",          -0.07536,  0.110175,  0.001542 },
        {"Black_H2",          -0.07536,  0.110175,  0.001542 },
        {"Black_N2",          -0.07536,  0.110175,  0.001542 },
        {"Black_ArMe",        -0.07536,  0.110175,  0.001542 },

        {"Blue_He",           -0.18249,  0.118666,  0.00128  },
        {"Blue_H2",           -0.24321,  0.196056,  0.003076 },
        {"Blue_N2",           -0.24321,  0.196056,  0.003076 },
        {"Blue_ArMe",         -0.24321,  0.196056,  0.003076 },

        {"Green_He",           0.206294, 1.33906,   0.026174 },
        {"Green_H2",           0.206294, 1.33906,   0.026174 },
        {"Green_N2",          -1.1521,   1.44788,   0.014481 },
        {"Green_ArMe",        -1.1521,   1.44788,   0.014481 },

        {"Red_He",             0.793182, 0.738671,  0.016    },
        {"Red_H2",             0.793182, 0.738671,  0.016    },
        {"Red_N2",             0.793182, 0.738671,  0.016    },
        {"Red_ArMe",           0.793182, 0.738671,  0.016    },

        {"Yellow_He",         -0.07536,  0.110175,  0.001542 },
        {"Yellow_H2",         -0.07536,  0.110175,  0.001542 },
        {"Yellow_N2",         -0.07536,  0.110175,  0.001542 },
        {"Yellow_ArMe",       -0.07536,  0.110175,  0.001542 },

        {"Green_Air",         -1.1521,   1.44788,   0.014481 },
        {"GreenBrown_Air",    -0.07536,  0.110175,  0.001542 },

        {"FPD2_Air",          -0.07536,  0.110175,  0.001542 },
        {"FPD2_O2",           -0.07536,  0.110175,  0.001542 },
        {"FPD1_H2",           -0.07536,  0.110175,  0.001542 },

        {"Red_H2",             3.37273,  1.23295,   0.035653 }, /* bad */
        {"Brown_He",           4.83566,  5.55065,   0.13015  }  /* bad */
        };



float pressure_to_flow ( float pressure,
                         struct frit_calib_struct *frit_calib )
{

    float flow;

      /* constants from Bob Henderson's MottFit routines are for PSI, SCCM */

    flow = frit_calib->parm1 +
           frit_calib->parm2 * pressure + 
           frit_calib->parm3 * pressure * pressure;

      /* now, convert flow to ml/min @NTP */

     flow = flow * T_NTP / T_STP; 
    
    if (flow < 0) flow = 0;

    return flow;

}



float flow_to_pressure ( float flow,
                         struct frit_calib_struct *frit_calib ) 
{
    float pressure;
    float delta;
    float calc_flow;

   
    if ( flow == 0 ) return 0;


        /* convert flow from ml/min@NTP to SCCM */

    /* flow = flow * T_STP/T_NTP; */

        /* now,   use succesive approximation to find the   */
        /* pressure that yeilds the desired flow based upon */
        /* frit parameters                                  */

    pressure = 50;      /* psi */
    delta = 26;         /*  sould allow us to reach 0-100 range */

    do
    {
        calc_flow = pressure_to_flow ( pressure, frit_calib );

        if ( calc_flow < flow ) pressure += delta;
        else                    pressure -= delta;

        delta = delta / 2;

        if ( delta <= .001 ) break;

     }  while ( fabs((double)flow-(double)calc_flow) > .001 );

    if ( pressure < 0 ) pressure = 0;
    if ( pressure > SENSOR_MAX_PSI ) pressure = SENSOR_MAX_PSI;

    return pressure;

}


short flow_to_digital ( unsigned long            flow,
                        struct ps_calib_struct   *ps_calib,
                        struct frit_calib_struct *frit_calib )
{

    float pressure;
    float flow_rate;
    short digital;
    float volts_per_psi;
    float sensor_voltage;
    float adc_out;
    float adc_in;

    if (display_debug) printf( "Flow: %8x  ", flow );

    flow_rate = (float)flow / (float)IU_SCALING;

    if (display_debug) printf ( "Rate: %8.2f  ", flow_rate );

    pressure = flow_to_pressure ( flow_rate, frit_calib );
   
    if ( pressure < 0 ) pressure = 0;
    if ( pressure > ps_calib->range) pressure = ps_calib->range;

    if (display_debug) printf ( "Pres: %4.2f  ", pressure );

    volts_per_psi = ps_calib->span / ps_calib->range;

    sensor_voltage = ps_calib->offset + (float)pressure * volts_per_psi;

    /* hard clip limits, just like hardware  */
    if ( sensor_voltage < 0 ) sensor_voltage = 0;
    if ( sensor_voltage > 4 ) sensor_voltage = 4;

    if (display_debug) printf( "Sensor: %4.2f  ", sensor_voltage );

    digital = (short)((sensor_voltage)*16384.0);
    if (sensor_voltage >= 4 ) digital = 0xffff;

    if (display_debug) printf( "digital: %6hx\n", digital );


    return digital;
}


unsigned long  digital_to_flow ( unsigned short digital,
                                 struct ps_calib_struct *ps_calib,
                                 struct frit_calib_struct *frit_calib )

{
    float sensor_voltage;
    float pressure;
    float flow;


    if (display_debug) printf ( "Digital: %6hx  ", digital );

    sensor_voltage  = (float)digital/16384.0;

    if (display_debug) printf( "sensor: %4.2f  ",  sensor_voltage );

    if (sensor_voltage <= SENSOR_OFFSET )
    {

          if (display_debug) printf( "Pres: %4.2f  ", 0 );
          if (display_debug) printf( "Flow: %8.2f\n", 0 );

          return (unsigned long)0;

    }

    pressure = (sensor_voltage - ps_calib->offset) * ps_calib->range / ps_calib->span;

    if ( pressure < 0 ) pressure = 0;
    if ( pressure > ps_calib->range) pressure = ps_calib->range;

    if (display_debug) printf( "Pres: %4.2f  ", pressure );

    flow = pressure_to_flow ( pressure , frit_calib );

    if (display_debug) printf( "Flow: %8.2f\n", flow );

    return (unsigned long)(flow*(float)IU_SCALING);

}

void print_flows_to_digital( float max_flow,
                             struct ps_calib_struct *ps_calib,
                             struct frit_calib_struct *frit_calib )
{

    short i;
    short num_entries;
    short step_rate;
    float          flow;
    short digital;
    unsigned long mask;
    short shift;

    if      ( max_flow <   31 ) { num_entries =  32;  step_rate = 1; mask = 0x001fff00 ; shift = 8; }
    else if ( max_flow <   63 ) { num_entries =  64;  step_rate = 1; mask = 0x003fff00 ; shift = 8; }
    else if ( max_flow <  127 ) { num_entries = 128;  step_rate = 1; mask = 0x007fff00 ; shift = 8; }
    else if ( max_flow <  255 ) { num_entries = 256;  step_rate = 1; mask = 0x00ffff00 ; shift = 8; }
    else if ( max_flow <  511 ) { num_entries = 256;  step_rate = 2; mask = 0x01fffe00 ; shift = 9; }
    else if ( max_flow < 1023 ) { num_entries = 256;  step_rate = 4; mask = 0x03fffc00 ; shift = 10;}
    else if ( max_flow < 2047 ) { num_entries = 256;  step_rate = 8; mask = 0x07fff800 ; shift = 11;}
    else if ( max_flow < 4095 ) { num_entries = 256;  step_rate =16; mask = 0x0ffff800 ; shift = 12;}
    else if ( max_flow < 8191 ) { num_entries = 256;  step_rate =32; mask = 0x1fffe000 ; shift = 13;}
    else if ( max_flow <16383 ) { num_entries = 256;  step_rate =64; mask = 0x3fffc000 ; shift = 14;}
    else if ( max_flow <32767 ) { num_entries = 256;  step_rate=128; mask = 0x7fff8000 ; shift = 15;}
    else                        { num_entries = 256;  step_rate=256; mask = 0xffff0000 ; shift = 16;}

    printf ( "\f" );
    printf ( "/*   max flow:  %6.1f ml/min @ %5.2f PSI            */\n\n",
             pressure_to_flow(ps_calib->range, frit_calib), ps_calib->range );

    printf ( "#define %s_MAX_FLOW    0x%-8x  /* internal units */\n", frit_calib->name,
             (long)pressure_to_flow(ps_calib->range, frit_calib)*IU_SCALING );

    printf ( "#define %s_TBL_MASK    0x%-8x\n", frit_calib->name, mask );

    printf ( "#define %s_SHIFT_VALUE 0x%-8x\n", frit_calib->name, shift );

    switch ( 256/num_entries )
    {
       case  1: printf ( "#define %s_ADC_SHIFT(ADC) ((ADC))\n", frit_calib->name );
                break;
       case  2: printf ( "#define %s_ADC_SHIFT(ADC) ((ADC)>>1)\n", frit_calib->name );
                break;
       case  4: printf ( "#define %s_ADC_SHIFT(ADC) ((ADC)>>2)\n", frit_calib->name );
                break;
       case  8: printf ( "#define %s_ADC_SHIFT(ADC) ((ADC)>>3)\n", frit_calib->name );
                break;
       case 16: printf ( "#define %s_ADC_SHIFT(ADC) ((ADC)>>4)\n", frit_calib->name );
                break;
       case 32: printf ( "#define %s_ADC_SHIFT(ADC) ((ADC)>>5)\n", frit_calib->name );
                break;
       case 64: printf ( "#define %s_ADC_SHIFT(ADC) ((ADC)>>6)\n", frit_calib->name );
                break;
    }

    printf ( "\nconst U_INT16 %s_Flow_To_Adc[] = {", frit_calib->name );

    for (i= 0 ; i<(num_entries-1) ; i++ )
    {
        flow = i*step_rate;

        digital = flow_to_digital((long)(flow*IU_SCALING), ps_calib, frit_calib );

        if (flow - step_rate > max_flow) break;


        if (display_debug != TRUE )
        {
            if ((i%8)==0)
            {
               if (display_as_real)
	          printf ( "\n/*%6.2f*/  %6.2f,", flow, (float)(digital+0x8000)/16384.0 );
               else
	          printf ( "\n/*%6.2f*/  0x%-6hx,", flow, digital );
            }
            else
            {
               if (display_as_real)
	          printf ( " %6.2f,", (float)(digital+0x8000)/16384.0 );
               else
	          printf ( " 0x%-6hx,", digital );
            }
       }
    }

    if (display_debug != TRUE )
    {
        if (display_as_real)
           printf ( " %6.2f };\n", (float)(digital+0x8000)/16384.0 );
        else
           printf ( " 0x%-6hx };\n", digital );
    }



}



void print_digital_to_flows ( float max_flow,
                              struct ps_calib_struct *ps_calib,
                              struct frit_calib_struct *frit_calib )
{

    short i;
    short num_entries;
    unsigned short digital;
    unsigned long flow;
    
    if      ( max_flow <  31 )  num_entries =  32;
    else if ( max_flow <  63 )  num_entries =  64;
    else if ( max_flow < 127 )  num_entries = 128;
    else                        num_entries = 256;

  
    if ( num_entries > 128 ) printf ( "\f" );

    printf ( "\n\nconst U_INT32 %s_Adc_To_Flow[] = {           ", frit_calib->name );
   
    for (i= 0 ; i<255 ; i+=(256/num_entries)) 
    {
        digital =  i*256;
        flow    = digital_to_flow(digital, ps_calib, frit_calib );

        if (display_debug != 1)
        {
            if (((i-4)%6)==0)
            {
               if (display_as_real)
                  printf ( "\n/*%6.2f */  %6.2f,", (float)(digital+0x8000)/16384.0, (float)flow/IU_SCALING );
               else
                  printf ( "\n/*%5hx */  0x%-8lx,", digital, flow );
            }
            else
            {
               if (display_as_real)
	          printf ( " %6.2f,", (float)flow/IU_SCALING );
               else
	          printf ( " 0x%-8lx,",flow );
            }
       }
    }

    digital = (i*256>(float)0x7fff)?(float)0x7fff:i*256;
    flow    = digital_to_flow(digital, ps_calib, frit_calib );

    if (display_debug != TRUE )
    {
        if (display_as_real)
            printf ( " %6.2f };\n", (float)flow/IU_SCALING );
        else
            printf ( " 0x%-8lx };\n",flow );
    }


}


int main(int argc, char *argv[] )
{

   short i;

   display_as_real = FALSE;
   display_debug   = FALSE;

   if ( argc >= 1 )
   {
      if ( strcmp( "-r", argv[1] ) == 0 )  display_as_real = TRUE;
      if ( strcmp( "-d", argv[1] ) == 0 )  display_debug   = TRUE;
   }


   printf ( "/* first, a few words about the sensor:             */\n");
   printf ( "/*     offset: %5.2f volts                          */\n", ps_calib.offset );
   printf ( "/*     span  : %5.2f volts                          */\n", ps_calib.span   );
   printf ( "/*     range : %5.2f PSI                            */\n", ps_calib.range  );
   printf ( "/*     volts/psi: %5.2f                             */\n", ps_calib.span/ps_calib.range );
   printf ( "/*     psi/volt : %5.2f                             */\n", ps_calib.range/ps_calib.span );


   for ( i=0 ; i< NUM_FRIT_ENTRIES ; i++ )
   {

      print_flows_to_digital ( pressure_to_flow(ps_calib.range, &frit_calib[i]), &ps_calib, &frit_calib[i] );

      print_digital_to_flows ( pressure_to_flow(ps_calib.range, &frit_calib[i]),
                               &ps_calib, &frit_calib[i] );

   }

return 0;

}

